package com.google.code.jtasks.model;

import java.util.Arrays;

import org.joda.time.DateTime;
import org.joda.time.Duration;

import com.google.code.jtasks.model.IMappedTask.ITaskComment;

/**
 * A mock {@link ITaskWorkEntry} implementation that can be used in tests.
 */
public final class MockTaskWorkEntry implements ITaskWorkEntry
{
	private final TaskPropertyValue<ITaskWorkEntry>[] propertyValues;

	/**
	 * Constructs a new {@link MockTaskWorkEntry} instance.
	 * 
	 * @param propertyValues
	 *            the {@link TaskPropertyValue}s that make up the {@link MockTaskWorkEntry}
	 */
	public MockTaskWorkEntry(TaskPropertyValue<ITaskWorkEntry>... propertyValues)
	{
		this.propertyValues = propertyValues;
	}

	/**
	 * Constructs a new {@link MockTaskWorkEntry} instance.
	 * 
	 * @param worker
	 *            the {@link ITaskWorkEntry#getWorker()} value to store in the {@link MockTaskWorkEntry}
	 * @param timeWorked
	 *            the {@link ITaskWorkEntry#getTimeWorked()} value to store in the {@link MockTaskWorkEntry}
	 */
	@SuppressWarnings("unchecked")
	public MockTaskWorkEntry(String worker, Duration timeWorked)
	{
		this(new TaskPropertyValue<ITaskWorkEntry>(WorkEntryField.WORKER, worker),
				new TaskPropertyValue<ITaskWorkEntry>(WorkEntryField.TIME_WORKED, timeWorked));
	}

	/**
	 * Returns the {@link TaskPropertyValue#getValue()} for the specified {@link ITaskProperty}, or
	 * <code>null</code> if no such {@link TaskPropertyValue} is found.
	 * 
	 * @param property
	 *            the {@link ITaskProperty} to find the {@link TaskPropertyValue} value for
	 * @return the {@link TaskPropertyValue#getValue()} for the specified {@link ITaskProperty}, or
	 *         <code>null</code> if no such {@link TaskPropertyValue} is found
	 */
	private Object findFieldValue(ITaskProperty<ITaskWorkEntry> property)
	{
		for (TaskPropertyValue<ITaskWorkEntry> propertyValue : propertyValues)
			if (propertyValue.getProperty().equals(property))
				return propertyValue.getValue();

		return null;
	}

	/**
	 * @see com.google.code.jtasks.model.ITaskWorkEntry#getCreationTime()
	 */
	@Override
	public DateTime getCreationTime()
	{
		return (DateTime) findFieldValue(WorkEntryField.CREATION_TIME);
	}

	/**
	 * @see com.google.code.jtasks.model.ITaskWorkEntry#getWorker()
	 */
	@Override
	public String getWorker()
	{
		return (String) findFieldValue(WorkEntryField.WORKER);
	}

	/**
	 * @see com.google.code.jtasks.model.ITaskWorkEntry#getTimeWorked()
	 */
	@Override
	public Duration getTimeWorked()
	{
		return (Duration) findFieldValue(WorkEntryField.TIME_WORKED);
	}

	/**
	 * @see com.google.code.jtasks.model.ITaskWorkEntry#getAssociatedComment()
	 */
	@Override
	public ITaskComment getAssociatedComment()
	{
		return (ITaskComment) findFieldValue(WorkEntryField.ASSOCIATED_COMMENT);
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode()
	{
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(propertyValues);
		return result;
	}

	/**
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj)
	{
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof ITaskWorkEntry))
			return false;
		ITaskWorkEntry other = (ITaskWorkEntry) obj;
		for (TaskPropertyValue<ITaskWorkEntry> propertyValue : propertyValues)
		{
			ITaskProperty<ITaskWorkEntry> property = propertyValue.getProperty();
			Object thisValue = propertyValue.getValue();
			Object otherValue = property.getValue(other);

			if (thisValue == null && otherValue != null)
				return false;
			else if (!thisValue.equals(otherValue))
				return false;
		}
		return true;
	}

	/**
	 * Stores the values of the {@link MockTaskWorkEntry} fields/properties.
	 * 
	 * @param <T>
	 *            the type of value stored in the {@link WorkEntryFieldValue}
	 */
	public static final class WorkEntryFieldValue<T>
	{
		private final String name;
		private final T value;

		/**
		 * Constructs a new {@link WorkEntryFieldValue} instance.
		 * 
		 * @param name
		 *            the name of the {@link MockTaskWorkEntry} field that this {@link WorkEntryFieldValue}
		 *            contains a value for
		 * @param value
		 *            the {@link MockTaskWorkEntry} field value to store in this {@link WorkEntryFieldValue}
		 */
		public WorkEntryFieldValue(String name, T value)
		{
			this.name = name;
			this.value = value;
		}

		/**
		 * Returns the name of the {@link MockTaskWorkEntry} field that this {@link WorkEntryFieldValue}
		 * contains a value for.
		 * 
		 * @return the name of the {@link MockTaskWorkEntry} field that this {@link WorkEntryFieldValue}
		 *         contains a value for
		 */
		public String getName()
		{
			return name;
		}

		/**
		 * Returns the {@link MockTaskWorkEntry} field value to store in this {@link WorkEntryFieldValue}.
		 * 
		 * @return the {@link MockTaskWorkEntry} field value to store in this {@link WorkEntryFieldValue}
		 */
		public T getValue()
		{
			return value;
		}
	}
}
